package MP.ASTs;
import MP.CompilationException;

public class AstPrinter implements Visitor {
	private int indent;

	public AstPrinter() {
		indent = 0;
	}

	private String indentString() {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < indent; ++i) {
			sb.append("  ");
		}
		return sb.toString();
	}

	void print(String s) {
		System.out.println(s);
	}

	public final void print(AST ast) throws CompilationException {
		ast.visit(this, null);
	}

	// ProgramAST
	public Object visitProgramAST(ProgramAST ast, Object o)
			throws CompilationException {
		print(indentString() + "Program(");
		++indent;
		ast.v.visit(this, o);
		ast.p.visit(this, o);
		ast.c.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	/*
	 * //ConstDeclAST public Object visitConstDeclAST(ConstDeclAST ast){
	 * ConstDeclAST cAst = (ConstDeclAST) ast; print(indentString() +
	 * "ConstDecl(" ); ++indent; print(indentString()+ cAst.name.Lexeme);
	 * cAst.l.visit(this,o); --indent; print(indentString()+")"); }
	 */
	// VarDeclPartAST
	public Object visitVarDeclPartAST(VarDeclPartAST ast, Object o)
			throws CompilationException {
		print(indentString() + "VarDeclPart(");
		++indent;
		ast.o.visit(this, o);
		ast.v.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// EmptyVarDeclPartAST
	public Object visitEmptyVarDeclPartAST(EmptyVarDeclPartAST ast, Object o)
			throws CompilationException {
		return null;
	}

	// OneDeclAST
	public Object visitVarDeclAST(OneDeclAST ast, Object o)
			throws CompilationException {
		VarDeclAST vAst = (VarDeclAST) ast;
		print(indentString() + "VarDecl(");
		++indent;
		print(indentString() + vAst.name.Lexeme);
		vAst.t.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// ConstDeclAST
	public Object visitConstDeclAST(OneDeclAST ast, Object o)
			throws CompilationException {
		ConstDeclAST cAst = (ConstDeclAST) ast;
		print(indentString() + "ConstDecl(");
		++indent;
		print(indentString() + cAst.name.Lexeme);
		cAst.l.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// ArrayTypeAST
	public Object visitArrayTypeAST(ArrayTypeAST ast, Object o)
			throws CompilationException {
		ArrayTypeAST aAst = (ArrayTypeAST) ast;
		print(indentString() + "ArrayType(");
		++indent;
		aAst.type.visit(this, o);
		print(indentString() + aAst.lower.Lexeme);
		print(indentString() + aAst.upper.Lexeme);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// EmptyExprListAST
	public Object visitEmptyExprListAST(EmptyExprListAST ast, Object o)
			throws CompilationException {
		return null;
	}

	// ExprListAST
	public Object visitExprListAST(ExprListAST ast, Object o)
			throws CompilationException {
		ExprListAST eAst = (ExprListAST) ast;
		print(indentString() + "ExprList(");
		++indent;
		eAst.e.visit(this, o);
		eAst.l.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// ProDeclPartAST
	public Object visitProDeclPartAST(ProDeclPartAST ast, Object o)
			throws CompilationException {
		ProDeclPartAST pAst = (ProDeclPartAST) ast;
		print(indentString() + "ProDeclPart(");
		++indent;
		pAst.o.visit(this, o);
		pAst.p.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// EmptyProDeclPartAST
	public Object visitEmptyProDeclPartAST(EmptyProDeclPartAST ast, Object o)
			throws CompilationException {
		return null;
	}

	// FuncDeclAST
	public Object visitFuncDeclAST(FuncDeclAST fAst, Object o)
			throws CompilationException {

		print(indentString() + "FuncDecl(");
		++indent;
		print(indentString() + fAst.name.Lexeme);
		fAst.para.visit(this, o);
		fAst.v.visit(this, o);
		fAst.p.visit(this, o);
		fAst.ret.visit(this, o);
		fAst.c.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// ProcDeclAST
	public Object visitProcDeclAST(ProcDeclAST ast, Object o)
			throws CompilationException {
		ProcDeclAST pAst = (ProcDeclAST) ast;
		print(indentString() + "ProcDecl(");
		++indent;
		print(indentString() + pAst.name.Lexeme);
		pAst.para.visit(this, o);
		pAst.v.visit(this, o);
		pAst.p.visit(this, o);
		pAst.c.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// ParaListAST
	public Object visitParaListAST(ParaListAST ast, Object o)
			throws CompilationException {
		ParaListAST pAst = (ParaListAST) ast;
		print(indentString() + "ParaList(");
		++indent;
		pAst.v.visit(this, o);
		pAst.p.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// EmptyParaListAST
	public Object visitEmptyParaListAST(EmptyParaListAST ast, Object o)
			throws CompilationException {
		return null;
	}

	// BoolTypeAST
	public Object visitBoolTypeAST(BoolTypeAST ast, Object o)
			throws CompilationException {
		print(indentString() + "Boolean");
		return null;
	}

	// IntTypeAST
	public Object visitIntTypeAST(IntTypeAST ast, Object o)
			throws CompilationException {
		print(indentString() + "Int");
		return null;
	}

	// RealTypeAST
	public Object visitRealTypeAST(RealTypeAST ast, Object o)
			throws CompilationException {
		print(indentString() + "Real");
		return null;
	}

	// StmtListAST
	public Object visitStmtListAST(StmtListAST ast, Object o)
			throws CompilationException {
		StmtListAST sAst = (StmtListAST) ast;
		print(indentString() + "StmtList(");
		++indent;
		sAst.o.visit(this, o);
		sAst.s.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// EmptyStmtListAST
	public Object visitEmptyStmtListAST(EmptyStmtListAST ast, Object o)
			throws CompilationException {
		return null;
	}

	// OneStmtAST
	public Object visitAssiStmtAST(AssiStmtAST ast, Object o)
			throws CompilationException {
		AssiStmtAST aAst = (AssiStmtAST) ast;
		print(indentString() + "AssiStmt(");
		++indent;
		aAst.l.visit(this, o);
		aAst.e.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// IfThenElseStmtAST
	public Object visitIfThenElseStmtAST(IfThenElseStmtAST ast, Object o)
			throws CompilationException {
		IfThenElseStmtAST iAst = (IfThenElseStmtAST) ast;
		print(indentString() + "IfThenElseStmt(");
		++indent;
		iAst.e.visit(this, o);
		iAst.s1.visit(this, o);
		iAst.s2.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// IfThenStmtAST
	public Object visitIfThenStmtAST(IfThenStmtAST ast, Object o)
			throws CompilationException {
		IfThenStmtAST iAst = (IfThenStmtAST) ast;
		print(indentString() + "IfThenStmt(");
		++indent;
		iAst.e.visit(this, o);
		iAst.s1.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// RepeatStmtAST
	public Object visitRepeatStmtAST(RepeatStmtAST ast, Object o)
			throws CompilationException {
		RepeatStmtAST rAst = (RepeatStmtAST) ast;
		print(indentString() + "RepeatStmt(");
		++indent;
		rAst.s.visit(this, o);
		rAst.e.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// RetNullStmtAST
	public Object visitRetNullStmtAST(RetNullStmtAST ast, Object o)
			throws CompilationException {
		print(indentString() + "Return()");
		return null;
	}

	// RetStmtAST
	public Object visitRetStmtAST(RetStmtAST ast, Object o)
			throws CompilationException {
		RetStmtAST rAst = (RetStmtAST) ast;
		print(indentString() + "RetStmt(");
		++indent;
		rAst.e.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// CallStmtAST
	public Object visitCallStmtAST(CallStmtAST ast, Object o)
			throws CompilationException {

		print(indentString() + "CallStmt(");
		++indent;
		print(indentString() + ast.name.Lexeme);
		ast.e.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// ForStmtAST
	public Object visitForStmtAST(ForStmtAST ast, Object o)
			throws CompilationException {
		ForStmtAST fAst = (ForStmtAST) ast;
		print(indentString() + "ForStmt(");
		++indent;
		print(indentString() + fAst.name.Lexeme);
		fAst.e1.visit(this, o);
		if (fAst.to)
			print(indentString() + "To");
		else
			print(indentString() + "DownTo");
		fAst.e2.visit(this, o);
		fAst.o.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// WhileStmtAST
	public Object visitWhileStmtAST(WhileStmtAST ast, Object o)
			throws CompilationException {
		WhileStmtAST wAst = (WhileStmtAST) ast;
		print(indentString() + "WhileStmt(");
		++indent;
		wAst.e.visit(this, o);
		wAst.o.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// BreakStmtAST
	public Object visitBreakStmtAST(BreakStmtAST ast, Object o)
			throws CompilationException {
		print(indentString() + "Break()");
		return null;
	}

	// CompStatAST
	public Object visitCompStatAST(CompStatAST ast, Object o)
			throws CompilationException {
		CompStatAST cAst = (CompStatAST) ast;
		print(indentString() + "CompStmt(");
		++indent;
		cAst.v.visit(this, o);
		cAst.s.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// ContStmtAST
	public Object visitContStmtAST(ContStmtAST ast, Object o)
			throws CompilationException {
		print(indentString() + "Continue()");
		return null;
	}

	public Object visitLvalueAST(LvalueAST ast, Object o)
			throws CompilationException {
		print(indentString() + "Lvalue(");
		++indent;
		print(indentString() + ast.name.Lexeme);
		if (!(ast.e instanceof EmptyExprListAST))
			ast.e.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// LiteralAST
	public Object visitIntLiteralAST(IntLiteralAST ast, Object o)
			throws CompilationException {

		print(indentString() + "IntLiteral(");
		++indent;
		print(indentString() + ast.literal.Lexeme);
		--indent;
		print(indentString() + ")");
		return null;
	}

	public Object visitRealLiteralAST(RealLiteralAST ast, Object o)
			throws CompilationException {

		print(indentString() + "RealLiteral(");
		++indent;
		print(indentString() + ast.literal.Lexeme);
		--indent;
		print(indentString() + ")");
		return null;
	}

	public Object visitBoolLiteralAST(BoolLiteralAST ast, Object o)
			throws CompilationException {

		print(indentString() + "BoolLiteral(");
		++indent;
		print(indentString() + ast.literal.Lexeme);
		--indent;
		print(indentString() + ")");
		return null;
	}

	public Object visitStringLiteralAST(StringLiteralAST ast, Object o)
			throws CompilationException {

		print(indentString() + "StringLiteral(");
		++indent;
		print(indentString() + ast.literal.Lexeme);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// UnaryExprAST
	public Object visitUnaryExprAST(UnaryExprAST ast, Object o)
			throws CompilationException {
		UnaryExprAST uAst = (UnaryExprAST) ast;
		print(indentString() + "UnaryExpr(");
		++indent;
		print(indentString() + uAst.op.Lexeme);
		uAst.e.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// BinExprAST
	public Object visitBinExprAST(BinExprAST ast, Object o)
			throws CompilationException {
		BinExprAST bAst = (BinExprAST) ast;
		print(indentString() + "BinExpr(");
		++indent;
		bAst.e1.visit(this, o);
		print(indentString() + bAst.op.Lexeme);
		bAst.e2.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// VarExprAST
	public Object visitVarExprAST(VarExprAST ast, Object o)
			throws CompilationException {
		VarExprAST vAst = (VarExprAST) ast;
		print(indentString() + "VarExpr(");
		++indent;
		print(indentString() + vAst.name.Lexeme);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// EleExprAST
	public Object visitEleExprAST(EleExprAST ast, Object o)
			throws CompilationException {
		EleExprAST eAst = (EleExprAST) ast;
		print(indentString() + "EleExpr(");
		++indent;
		print(indentString() + eAst.name.Lexeme);
		eAst.e.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// CallExprAST
	public Object visitCallExprAST(CallExprAST ast, Object o)
			throws CompilationException {
		CallExprAST cAst = (CallExprAST) ast;
		print(indentString() + "CallExpr(");
		++indent;
		print(indentString() + cAst.name.Lexeme);
		cAst.e.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}

	// CastInt2RealAST
	public Object visitCastInt2RealAST(CastInt2RealAST ast, Object o)
			throws CompilationException {

		print(indentString() + "CastInt2Real(");
		++indent;
		ast.e.visit(this, o);
		--indent;
		print(indentString() + ")");
		return null;
	}
}
